/*
 * Copyright 1999,2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.webduff.methods;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import com.googlecode.webduff.MimeTyper;
import com.googlecode.webduff.WebDuffConfiguration;
import com.googlecode.webduff.io.URI;
import com.googlecode.webduff.locking.AcquiredLock;
import com.googlecode.webduff.locking.AtomicOperationAcquiredLock;
import com.googlecode.webduff.locking.ClientManagedAcquiredLock;
import com.googlecode.webduff.locking.LockFailedException;
import com.googlecode.webduff.locking.ResourceLocks;
import com.googlecode.webduff.store.WebdavStore;

public abstract class AbstractMethod implements MethodExecutor {
	
	protected WebDuffConfiguration servletConfig;
	
	protected WebdavStore store;
	
	protected ResourceLocks resourceLocks;

	protected MethodRegistry methodsRegistry;
	
	protected MimeTyper theMimeTyper;
	
	protected ServletContext theServletContext;

	public void init(WebDuffConfiguration config, MethodRegistry registry, WebdavStore store, ResourceLocks locks, MimeTyper mimeTyper, ServletContext servletContext) {
		servletConfig = config;
		methodsRegistry = registry;
		this.store = store;
		resourceLocks = locks;
		theMimeTyper = mimeTyper;
		theServletContext = servletContext;
	}
	
	protected MethodExecutor getMethod(String methodName) {
		return methodsRegistry.getMethod(methodName, servletConfig, methodsRegistry, store, resourceLocks, theMimeTyper, theServletContext);
	}
    
    /**
     * size of the io-buffer
     */
    protected static int BUF_SIZE = 50000;
    /**
     * Return the relative path associated with this servlet.
     * 
     * @param request
     *            The servlet request we are processing
     */
    protected String getRelativePath(HttpServletRequest request) {
        // Are we being processed by a RequestDispatcher.include()?
        if (request.getAttribute("javax.servlet.include.request_uri") != null) {
            String result = (String) request
                    .getAttribute("javax.servlet.include.path_info");
//            if (result == null)
//                result = (String) request
//                        .getAttribute("javax.servlet.include.servlet_path");
            if ((result == null) || (result.equals("")))
                result = "/";
            return (result);
        }

        // No, extract the desired path directly from the request
        String result = request.getPathInfo();
//        if (result == null) {
//            result = request.getServletPath();
//        }
        if ((result == null) || (result.equals(""))) {
            result = "/";
        }
        return (result);
    }
    
    protected URI getURIForRequest(HttpServletRequest aRequest) {
    	String theResult = null;
    	
    	if (aRequest.getAttribute("javax.servlet.include.request_uri") != null) {
            theResult = (String) aRequest.getAttribute("javax.servlet.include.path_info");
		} else {
			theResult = aRequest.getPathInfo();
		}
    	
    	if ((theResult == null) || (theResult.equals(""))) {
	        theResult = "/";
        }
    	
    	return new URI(theResult);
    }
    
    protected URI getContextURI() {
    	return new URI(theServletContext.getContextPath());
    }
    
    private String getLockOwner(HttpServletRequest theRequest) {
    	return getClass().getCanonicalName() + System.currentTimeMillis() + theRequest.toString();
    }
    
    protected AcquiredLock acquireLock(URI uri, int depth, boolean isExclusive, HttpServletRequest aRequest) throws LockFailedException {
    	String lockId = getLockIdFromRequest(aRequest);
    	if(lockId != null && resourceLocks.getLockByID(lockId) != null) {
    		return new ClientManagedAcquiredLock();
    	}
    	String lockOwner = getLockOwner(aRequest);
    	return new AtomicOperationAcquiredLock(resourceLocks, resourceLocks.lock(uri, lockOwner, isExclusive, depth), lockOwner);
    }
    
    protected String getLockIdFromRequest(HttpServletRequest request) {
    	if(request.getHeader("if") != null) {
        	Matcher aMatcher = Pattern.compile("\\(\\<opaquelocktoken\\:([a-zA-Z0-9\\-]+)\\>\\)").matcher(request.getHeader("if"));
        	if(aMatcher.matches()) {
        		return aMatcher.group(1);
        	}
        }
    	if(request.getHeader("lock-token") != null) {
        	Matcher aMatcher = Pattern.compile("\\<opaquelocktoken\\:([a-zA-Z0-9\\-]+)\\>").matcher(request.getHeader("lock-token"));
        	if(aMatcher.matches()) {
        		return aMatcher.group(1);
        	}
        }
		return null;
	}

	protected AcquiredLock acquireSharedLock(URI uri, int depth, HttpServletRequest aRequest) throws LockFailedException {
    	return acquireLock(uri, depth, false, aRequest);
    }
    
    protected AcquiredLock acquireExclusiveLock(URI uri, int depth, HttpServletRequest aRequest) throws LockFailedException {
    	return acquireLock(uri, depth, true, aRequest);
    }
    
    /**
     * creates the parent path from the given path by removing the last '/' and
     * everything after that
     *
     * @param path
     *            the path
     * @return parent path
     */
    public String getParentPath(String path) {
        int slash = path.lastIndexOf('/');
        if (slash != -1) {
            return path.substring(0, slash);
        }
        return null;
    }

}